package com.francis.springbootcommonutil.mappoint;


import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.List;


/**
 * <p>Title: MapUtil</p>
 * <p>Description: MapUtil 判断坐标范围，地图，经纬度范围</p>
 * <p>Copyright: ChenWei Copyright(c) 2018/p>
 * @author  ChenWei
 * @version 0.0.1
 * <pre>Histroy:
 *       2020/6/22 16:07 Create by ChenWei
 *</pre>
 */
public class MapUtil {


    public static void main(String[] args) {
//        long lo = System.currentTimeMillis();
        Point2D.Double point = new Point2D.Double(88.889999, 88.889999);
        List<Point2D.Double> pts = new ArrayList<Point2D.Double>();
        pts.add(new Point2D.Double(116.396, 39.910));
        pts.add(new Point2D.Double(116.395, 39.910));
        pts.add(new Point2D.Double(116.403, 39.920));
//        pts.add(new Point2D.Double(116.402, 39.914));
//        pts.add(new Point2D.Double(116.410, 39.913));
//        pts.add(new Point2D.Double(116.395, 39.910));
        if (isPtInPoly(point, pts)) {
            System.out.println("点在多边形内");
        } else {
            System.out.println("点在多边形外");
        }


    }


    // 地球半径
    private static double EARTH_RADIUS = 6378.137;

    private static double rad(double d) {
        return d * Math.PI / 180.0;
    }

    /**
     * @param point 检测点
     * @param pts   多边形的顶点
     * @return boolean  点在多边形内返回true, 否则返回false
     * @methodName isPtInPoly
     * @description 判断点是否在多边形内
     * @author ChenWei
     * @date 2020/6/22 16:00
     */
    public static boolean isPtInPoly(Point2D.Double point, List<Point2D.Double> pts) {
        int N = pts.size();
        //如果点位于多边形的顶点或边上，也算做点在多边形内，直接返回true
        boolean boundOrVertex = true;
        int intersectCount = 0;
        //浮点类型计算时候与0比较时候的容差
        double precision = 2e-10;
        Point2D.Double p1, p2;
        Point2D.Double p = point;
        p1 = pts.get(0);
        for (int i = 1; i <= N; ++i) {
            if (p.equals(p1)) {
                return boundOrVertex;
            }
            p2 = pts.get(i % N);
            if (p.x < Math.min(p1.x, p2.x) || p.x > Math.max(p1.x, p2.x)) {
                p1 = p2;
                continue;

            }
            if (p.x > Math.min(p1.x, p2.x) && p.x < Math.max(p1.x, p2.x)) {
                if (p.y <= Math.max(p1.y, p2.y)) {
                    if (p1.x == p2.x && p.y >= Math.min(p1.y, p2.y)) {
                        return boundOrVertex;
                    }
                    if (p1.y == p2.y) {
                        if (p1.y == p.y) {
                            return boundOrVertex;
                        } else {
                            ++intersectCount;
                        }

                    } else {
                        double xinters = (p.x - p1.x) * (p2.y - p1.y) / (p2.x - p1.x) + p1.y;
                        if (Math.abs(p.y - xinters) < precision) {
                            return boundOrVertex;
                        }
                        if (p.y < xinters) {
                            ++intersectCount;
                        }
                    }
                }

            } else {
                if (p.x == p2.x && p.y <= p2.y) {
                    Point2D.Double p3 = pts.get((i + 1) % N);
                    if (p.x >= Math.min(p1.x, p3.x) && p.x <= Math.max(p1.x, p3.x)) {
                        ++intersectCount;
                    } else {
                        intersectCount += 2;
                    }
                }
            }
            p1 = p2;
        }

        if (intersectCount % 2 == 0) {
            //偶数在多边形外
            return false;
        } else {
            //奇数在多边形内
            return true;
        }
    }

    /**
     * @param pts 坐标点集合
     * @return double
     * @methodName maxDist
     * @description 计算多个坐标点之间的最大距离
     * @author ChenWei
     * @date 2020/6/22 16:01
     */
    public static double maxDist(List<Point2D.Double> pts) {
        Point2D.Double initial;
        if (pts.isEmpty()) {
            return 0;
        } else {
            initial = pts.get(0);
        }
        //精度
        Point2D.Double maxLongitude = initial;
        Point2D.Double minLongitude = initial;
        double longitude;
        //纬度
        Point2D.Double maxLatitude = initial;
        Point2D.Double minLatitude = initial;
        double latitude;

        for (Point2D.Double pd : pts) {
            latitude = pd.x;
            longitude = pd.y;
            if (longitude > maxLongitude.y) {
                maxLongitude = pd;
            }
            if (longitude < minLongitude.y) {
                minLongitude = pd;
            }
            if (latitude > maxLatitude.x) {
                maxLatitude = pd;
            }
            if (latitude < minLatitude.x) {
                minLatitude = pd;
            }
        }
        double x = getDistance(maxLongitude, minLongitude);
        double y = getDistance(maxLatitude, minLatitude);
        return x > y ? x : y;
    }


    /**
     * @param p, c
     * @return java.lang.String
     * @methodName distencePC
     * @description 判断点是否在圆形内 ，判断点与圆心之间的距离和圆半径的关系
     * @author ChenWei
     * @date 2020/6/22 16:02
     */
    public static String distencePC(Point2D p, Circle c) {//
        String s;
        double d2 = Math.hypot((p.getX() - c.getCC().getX()), (p.getY() - c.getCC().getY()));
        System.out.println("d2==" + d2);
        double r = c.getR();
        if (d2 > r) {
            s = "圆外";
        } else if (d2 < r) {
            s = "圆内";
        } else {
            s = "圆上";
        }
        return s;
    }


    /**
     * @param p, c
     * @return java.lang.String
     * @methodName distencePC
     * @description 判断点是否在圆形内 ，判断点与圆心之间的距离和圆半径的关系
     * @author ChenWei
     * @date 2020/6/22 16:02
     */
    public static boolean isCircle(Point2D p, Circle c) {
        boolean result;
        double d2 = Math.hypot((p.getX() - c.getCC().getX()), (p.getY() - c.getCC().getY()));
        System.out.println("d2==" + d2);
        double r = c.getR();
        if (d2 > r) {
            result = false;
        } else if (d2 < r) {
            result = true;
        } else {
            result = true;
        }
        return result;
    }


    /**
     * @param pd1, pd2
     * @return double
     * @methodName getDistance
     * @description 通过经纬度获取距离(单位 ： 米)  计算两个坐标点的距离
     * @author ChenWei
     * @date 2020/6/22 16:04
     */
    public static double getDistance(Point2D.Double pd1, Point2D.Double pd2) {
        double lat1 = pd1.y;
        double lng1 = pd1.x;
        double lat2 = pd2.y;
        double lng2 = pd2.x;
        double radLat1 = rad(lat1);
        double radLat2 = rad(lat2);
        double a = radLat1 - radLat2;
        double b = rad(lng1) - rad(lng2);
        double s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2)
                + Math.cos(radLat1) * Math.cos(radLat2)
                * Math.pow(Math.sin(b / 2), 2)));
        s = s * EARTH_RADIUS;
        s = Math.round(s * 10000d) / 10000d;
        s = s * 1000;
        return s;
    }


    /**
     * 通过经纬度获取距离(单位：米)
     *
     * @param lat1
     * @param lng1
     * @param lat2
     * @param lng2
     * @return
     */
    public static double getDistance(double lat1, double lng1, double lat2,
                                     double lng2) {
        double radLat1 = rad(lat1);
        double radLat2 = rad(lat2);
        double a = radLat1 - radLat2;
        double b = rad(lng1) - rad(lng2);
        double s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) +
                Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b / 2), 2)));
        s = s * EARTH_RADIUS;
        s = Math.round(s * 10000d) / 10000d;
        return s;
    }

    /**
     * 判断一个点是否在圆形区域内
     */
    public static boolean isInCircle(double lng1, double lat1, double lng2, double lat2, String radius) {
        return getDistance(lat1, lng1, lat2, lng2) > Double.parseDouble(radius);
    }

}
